//
//  ACDiskCache.m
//  ACCache_Example
//
//  Created by autohome on 2021/8/7.
//  Copyright © 2021 CaiXiang. All rights reserved.
//

#import "ACDiskCache.h"
#import "ACDiskStorage.h"

#define ACLock() dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER)
#define ACUnlock() dispatch_semaphore_signal(self->_lock)

@implementation ACDiskCache {
    ACDiskStorage *_ds;
    dispatch_semaphore_t _lock;
}

- (nullable instancetype)initWithPath:(NSString *)path {
    self = [super init];
    if (!self) {
        return nil;
    }
    ACDiskStorage *ds = [[ACDiskStorage alloc] initWithPath:path];
    if (!ds) return nil;
    _ds = ds;
    _lock = dispatch_semaphore_create(1);
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appWillBeTerminated) name:UIApplicationWillTerminateNotification object:nil];
    return self;
}

- (BOOL)containsObjectForKey:(NSString *)key {
    if (!key) return NO;
    ACLock();
    BOOL contains = [_ds existsCacheWithCacheKey:key];
    ACUnlock();
    return contains;
}

- (nullable id)objectForKey:(NSString *)key {
    if (!key) return nil;
    ACLock();
    NSData *archiverData = [_ds selectCacheData:key];
    ACUnlock();
    if (!archiverData) return nil;
    
    id object = nil;
    @try {
          if (@available(iOS 11.0, *)) {
              NSError *error;
              NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:archiverData error:&error];
              unarchiver.requiresSecureCoding = NO;
              object = [unarchiver decodeTopLevelObjectForKey:NSKeyedArchiveRootObjectKey error:&error];
              if (error) {
                  NSLog(@"NSKeyedUnarchiver unarchive failed with error: %@", error);
              }
          } else {
              object = [NSKeyedUnarchiver unarchiveObjectWithData:archiverData];
          }
    } @catch (NSException *exception) {
    }
    return object;
}

- (void)setObjects:(NSArray<id<NSCoding>>*)objects forKeys:(NSArray <NSString *>*)keys {
    if (objects.count != keys.count || keys.count == 0 || objects.count == 0) {
        return;
    }
    NSMutableArray *datas = [@[] mutableCopy];
    for (id <NSCopying> obj in objects) {
        NSData *archiverData = nil;
        @try {
            if (@available(iOS 11.0, *)) {
                    NSError *error = nil;
                    archiverData = [NSKeyedArchiver archivedDataWithRootObject:obj requiringSecureCoding:NO error:&error];
               } else {
                    archiverData = [NSKeyedArchiver archivedDataWithRootObject:obj];
               }
        }
        @catch (NSException *exception) {
        }
        if (!archiverData) return;
        [datas addObject:archiverData];
    }
    ACLock();
    [_ds saveCacheDatas:datas forKey:keys];
    ACUnlock();
}

- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key {
    if (!key) return;
    if (!object) {
        ACLock();
        [_ds deleteCacheData:key];
        ACUnlock();
        return;
    }
    NSData *archiverData = nil;
    @try {
        if (@available(iOS 11.0, *)) {
                NSError *error = nil;
                archiverData = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:NO error:&error];
           } else {
                archiverData = [NSKeyedArchiver archivedDataWithRootObject:object];
           }
    }
    @catch (NSException *exception) {
    }
    if (!archiverData) return;
    ACLock();
    [_ds saveCacheData:archiverData forKey:key];
    ACUnlock();
}

- (void)removeObjectForKey:(NSString *)key {
    if (!key) return;
    ACLock();
    [_ds deleteCacheData:key];
    ACUnlock();
}

- (void)removeAllObjects {
    ACLock();
    [_ds deleteAllCacheData];
    ACUnlock();
}

- (void)_appWillBeTerminated {
    ACLock();
    [_ds removeExpiredCache];
    _ds = nil;
    ACUnlock();
}

- (void)setEffectiveCacheTime:(NSTimeInterval)effectiveCacheTime {
    _effectiveCacheTime = effectiveCacheTime;
    _ds.effectiveCacheTime = effectiveCacheTime;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil];
#if DEBUG
    NSLog(@"%s",__func__);
#endif
}

@end
